#include "rio.h"

#include <string.h>
#include <errno.h>

ssize_t
rio_readn(int fd, void *buf, size_t count)
{
    size_t left = count;
    ssize_t n;
    char *ptr = buf;

    while (left > 0) {
        n = read(fd, ptr, left);
        if (n < 0) {
            if (errno == EINTR)
                n = 0;
            else
                return n;
        } else if (n == 0) {
            // EOF
            break;
        }

        left -= n;
        ptr += n;
    }

    return count - left;
}

ssize_t
rio_writen(int fd, const void *buf, size_t count)
{
    size_t left = count;
    ssize_t n;
    const char *ptr = buf;

    while (left > 0) {
        n = write(fd, ptr, left);
        if (n <= 0) {
            if (errno == EINTR)
                n = 0;
            else
                return n;
        }

        left -= n;
        ptr += n;
    }

    return count;
}

void
rio_readinitb(RIOT *rp, int fd)
{
    rp->fd = fd;
    rp->unread_count = 0;
    rp->unread_ptr = rp->buf;
}

/*
 * 同 read 函数具有相同的语语义，最多读取 count 字节到 buf  中，
     unread_count  > 0: 则复制缓冲区中的到 buf 中，并返回已读取的字节数
     unread_count <= 0: 则调用 read 填充缓冲区，再复制数据到 buf 中
 * 返回值：
      > 0: 已读取的字节数
     == 0: 出现 EOF
      < 0: 出现错误，可查看 errno
**/
static ssize_t
rio_read(RIOT *rp, void *buf, size_t count)
{
    ssize_t n;
    while (rp->unread_count <= 0) {
        n = read(rp->fd, rp->buf, sizeof(rp->buf));
        if (n < 0) {
            if (errno == EINTR)
                n = 0;
            else
                return n;
        } else if (n == 0) {
            return n; // EOF
        }

        rp->unread_count = n;
        rp->unread_ptr = rp->buf;
    }

    size_t tmp = rp->unread_count;
    n = tmp >= count ? count : tmp;
    memcpy(buf, rp->unread_ptr, n);
    rp->unread_count -= n;
    rp->unread_ptr += n;
    return n;
}

ssize_t
rio_readnb(RIOT *rp, void *buf, size_t count)
{
    size_t left = count;
    ssize_t n;
    char *ptr = buf;

    while (left > 0) {
        n = rio_read(rp, ptr, left);
        if (n < 0)
            return n;
        else if (n == 0)
            break;
        ptr += n;
        left -= n;
    }

    return count - left;
}

ssize_t
rio_readlineb(RIOT *rp, void *buf, size_t count)
{
    size_t i;
    ssize_t n;
    char *ptr = buf;;

    // 调用 rio_read 从缓冲区中读取 1 字节，避免调用 read 每次读取 1 字节都陷入到内核中。
    for (i = 1; i < count; i++) {
        n = rio_read(rp, ptr, 1);    
        if (n < 0)
            return n;
        else if (n == 0)
            break;

        if (*ptr++ == '\n') {
            i++;
            break;
        }
    }
    *ptr = 0;
    return i - 1;
}

